#pragma once
#include "requestor.hpp"

namespace MyRpc
{
    namespace client
    {
        // 客户端这个Provider类是为了注册方法给服务端
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr& r)
                :_requestor(r)
            {}
            bool registerMethod(const BaseConnection::ptr& conn, const std::string& method, const Addr& host)
            {
                // 构造请求
                BaseMessage::ptr req = MessageFactory::create(MType::REQ_SERVICE);
                auto rpc_req = std::dynamic_pointer_cast<ServiceRequest>(req);
                rpc_req->setHost(host);
                rpc_req->setId(UUID::uuid());
                rpc_req->setMethod(method);
                rpc_req->setMType(MType::REQ_SERVICE);
                rpc_req->setOptype(ServiceRequestOpType::SERVICE_REGISTRY);

                // 发送注册服务请求
                BaseMessage::ptr rsp;
                bool flag = _requestor->send(conn, rpc_req, rsp);
                if(flag == false)
                {
                    // 发送消息出错
                    LOG(DEBUGLEVEL, "发送消息出错");
                    return false;
                }
                // 走到这块发送消息成功
                ServiceResponse::ptr rpc_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                if(rpc_rsp->rcode() != RCode::RCODE_OK)
                {
                    // 表示注册失败
                    LOG(DEBUGLEVEL, "服务注册失败 %s", errReason(rpc_rsp->rcode()).c_str());
                    return false;
                }
                LOG(DEBUGLEVEL, "服务注册成功");
                return true;
            }
        private:
            Requestor::ptr _requestor;
        };
        
        class MethodHosts
        {
        public:
            using ptr = std::shared_ptr<MethodHosts>;
            MethodHosts() 
                :_pos(0)
            {}
            MethodHosts(const std::vector<Addr>& addrs)
                :_hosts(addrs)
                ,_pos(0)
            {}
            void appendHost(const Addr& host)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                _hosts.push_back(host);
            }
            // 服务下线之后删除这个服务
            void removeHost(const Addr& host)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                for(auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if(*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }
            Addr selectMethod()
            {
                std::lock_guard<std::mutex> lock(_mtx);
                int size = _hosts.size();
                return _hosts[_pos++ % size];
            }
            bool empty()
            {
                std::lock_guard<std::mutex> lock(_mtx);
                return _hosts.empty();
            }
        private:
            std::mutex _mtx;
            std::vector<Addr> _hosts;  // 储存所有的关于这个方法的主机信息
            int _pos;   // 用来指向哪个特定主机的索引
        };

        class Discovery
        {
        public:
            using OffLineCallBack = std::function<void(const Addr&)>;
            using ptr = std::shared_ptr<Discovery>;
            Discovery(const Requestor::ptr& r, const OffLineCallBack& cb)
                :_requestor(r)
                ,_offlinecb(cb)
            {}
            // 获取一个对应方法的主机方便外界访问
            bool ServiceDiscovery(const BaseConnection::ptr& conn, const std::string& method, Addr& addr)
            {
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    auto it = _method_hosts.find(method);
                    if(it != _method_hosts.end())
                    {
                        if(it->second->empty() == false)
                        {
                            addr = it->second->selectMethod();
                            return true;
                        }
                    }
                }

                LOG(DEBUGLEVEL, "没有找到这个服务");
                // 就要构造请求去服务端发现这个服务
                // 构造请求
                auto req = MessageFactory::create(MType::REQ_SERVICE);
                auto msg_req = std::dynamic_pointer_cast<ServiceRequest>(req);
                std::string id = UUID::uuid();
                msg_req->setId(id);
                // LOG(DEBUGLEVEL, "id : %s", id.c_str());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setMethod(method);
                msg_req->setOptype(ServiceRequestOpType::SERVICE_DISCOVERY);
                BaseMessage::ptr rsp;
                bool flag = _requestor->send(conn, msg_req, rsp);
                if (flag == false)
                {
                    LOG(DEBUGLEVEL, "请求失败");
                    return false;
                }
                // 走到这块就是收到应答 收到应答之时他肯定是没有这个服务主机对象的 所以直接构造对象给他即可
                auto rpc_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                if (rpc_rsp->rcode() != RCode::RCODE_OK)
                {
                    LOG(DEBUGLEVEL, "应答是错误的 %s", errReason(rpc_rsp->rcode()).c_str());
                    return false;
                }
                // 拿到这块服务应答的主机信息放到这个方法管理的hash表中
                _method_hosts[method] = std::make_shared<MethodHosts>(rpc_rsp->hosts());

                std::lock_guard<std::mutex> lock(_mtx);
                if(_method_hosts[method]->empty())
                {
                    // 通过请求之后主机信息仍然为空就直接退出
                    LOG(DEBUGLEVEL, "请求服务主机没有");
                    return false;
                }
                // 走到这块就直接可以取出来一个主机信息了
                addr = _method_hosts[method]->selectMethod();
                LOG(DEBUGLEVEL, "服务发现成功!");
                return true;
            }
            // 注册给dispatcher模块
            void onServiceRequest(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                // 先拿到这个请求的类型 1. 服务上线请求 2.服务下线请求
                auto opty = msg->optype();
                if(opty == ServiceRequestOpType::SERVICE_ONLINE)
                {
                    // 服务上线
                    auto it = _method_hosts.find(msg->method());
                    if(it == _method_hosts.end())
                    {
                        // 没有找到这个服务
                        auto method_host = std::make_shared<MethodHosts>();
                        _method_hosts[msg->method()] = method_host;
                        method_host->appendHost(msg->host());
                    }
                    else
                    {
                        // 有这个服务 只需要添加主机即可
                        _method_hosts[msg->method()]->appendHost(msg->host());
                    }
                }
                else if(opty == ServiceRequestOpType::SERVICE_OFFLINE)
                {
                    // 服务下线
                    auto it = _method_hosts.find(msg->method());
                    if(it == _method_hosts.end())
                    {
                        // 没有这服务直接退出
                        return;
                    }
                    // 走到这块有这个服务 直接移除这个服务主机即可
                    _method_hosts[msg->method()]->removeHost(msg->host());
                    _offlinecb(msg->host());
                }
                else
                {
                    LOG(DEBUGLEVEL, "请求错误");
                }
            }
        private:
            std::mutex _mtx;
            OffLineCallBack _offlinecb;   // 增加一个删除主机信息的回调函数
            std::unordered_map<std::string, MethodHosts::ptr> _method_hosts;
            Requestor::ptr _requestor;
        };
    }
}